package thread;

/**
 * Creared with IntelliJ IDEA.
 * Description:单例模型 饿汉模式,懒汉模式
 * User:yxd
 * Date:2022-03-28
 * Time:19:23
 */
//通过Singleton这个类来实现单例模式,保证Singleton这个类只有唯一实例

class Singleton{//饿汉模式
    //使用static创建一个实例,并且立即进行实例化(比较着急)
    private static Singleton instance;//类成员,一般类对象只存在一份(和类相关,和对象无关)
    //为了防止程序员在其他地方不小心的new Singleton,我们把这个构造方法设置为private的不能new
    private Singleton(){}
    //提供方法是在类外能获得到这个实例
    public static Singleton getInstance(){
        return instance;
    }
}
class Singleton1{//懒汉模式
    //不是立即初始化实例
    private volatile static Singleton1 instance = null;//volatile关键字一定要加保证第一次的判断不会出错,不会出现不该加锁的加锁的内存可见性问题
    //设置构造方法
    private Singleton1(){}
    //得到实例的方法
    public  static Singleton1 getInstance1(){
        synchronized (Singleton1.class){//将整个过程进行加锁,不能加在if里面,放到里面就没有起到原子性了,使用类对象(只有一个)来进行加锁,
            // 但是这样又引入了新的问题:我们线程不安全只是第一次的时候(需要读和改)是线程不安全的,后面初始化了之后就只剩下读操作了,就没有线程安全的问题了,
            // 而每次调用getInstance方法时都需要加锁,也就有可能产生锁的竞争,但是这样的竞争是没有必要的,因此我们换需要再加条件,让初始化后就不用加锁了
            if(instance == null){
                instance = new Singleton1();//只有用到的时候才去创建
            }
            return instance;
        }
    }
    //最正确完美的获得实例方法
    public static Singleton1 getInstance(){//如果多个线程,都去调用这里的getIntance方法的话,就会造成大量的度instance内存的操作,
        // 可能会让编译器把这个读内存操作给优化成读寄存器,如果修改了instance后面线程就都感知不到,仍然当成null,
        // 那么就又出现内存可见性问题了,就又有线程安全问题了,对第二个判定条件问题不大,但是会造成第一个条件的误判导致给不该加锁的加锁了,那就需要加volatile关键字了
        if(instance == null){//第一次看是否初始化了,是否存在线程安全,是否需要加锁
            synchronized (Singleton1.class){
                if(instance == null){//
                    instance = new Singleton1();//只有用到的时候才去创建
                }
            }
        }
        return instance;
    }
    public static Singleton1 getInstance2(){  //懒汉模式不仅读变量,而且进行了判断,也进行了修改,而且这不是原子的,很容易就会乱序,就存在线程安全问题了下面看一下如何修改
        if(instance == null){
            instance = new Singleton1();//只有用到的时候才去创建
        }
        return instance;
    }

}
public class Demo20 {
    public static void main(String[] args) {
        Singleton s1 = Singleton.getInstance();//获得到饿汉模式的唯一实例
        //Singleton s1 = new Singleton();//这是不行
        Singleton1 s2 = Singleton1.getInstance();//获得懒汉模式的唯一实例


    }
}
